"""
环境由三部分组成
1、世界表达
2、ai通讯
3、物理引擎
"""
import logging
import random

from flask import Flask, request, render_template, jsonify
from datetime import timedelta

log = logging.getLogger('werkzeug')
log.disabled = True


class MapEnum:
  NULL = 0
  GIFT = 1
  DAMAGE = 2
  AI = 3


D_INDEX_VECTOR = [
  [0, 1],
  [1, 1],
  [1, 0],
  [1, -1],
  [0, -1],
  [-1, -1],
  [-1, 0],
  [-1, 1],
  [0, 0],
]
"""
[0, 1],
[1, 1],
[1, 0],
[1, -1],
[0, -1],
[-1, -1],
[-1, 0],
[-1, 1],
[0, 0],
"""


D_INDEX_VECTOR_16 = [
  [0, 1],
  [1, 1],
  [1, 0],
  [1, -1],
  [0, -1],
  [-1, -1],
  [-1, 0],
  [-1, 1],
  [0, 0],
  [0, 2],
  [2, 2],
  [2, 0],
  [2, -2],
  [0, -2],
  [-2, -2],
  [-2, 0],
  [-2, 2],
]


class World(Flask):
  def __init__(self, width=5, height=5, giftCount=1, damageCount=1, dIndexVector=None, shuffle=False):
    super().__init__(self.__class__.__name__, template_folder='./templates')
    if dIndexVector is None:
      dIndexVector = D_INDEX_VECTOR
    # 自动重载模板文件
    self.jinja_env.auto_reload = True
    self.config['TEMPLATES_AUTO_RELOAD'] = True
    self.map = [[0 for _ in range(width)] for _ in range(height)]
    # 设置静态文件缓存过期时间
    self.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1)
    self.ai = {'hp': 1.0, 'move': [0.0 for _ in range(8)] + [1.0], 'y': 0.0}
    for _ in range(giftCount):
      self.SpawnRes([], MapEnum.GIFT)
    for _ in range(damageCount):
      self.SpawnRes([], MapEnum.DAMAGE)
    self.last_loss_t = 0
    self.t = 0
    self.dIndexVector = dIndexVector
    if shuffle:
      random.shuffle(self.dIndexVector)
    self.randomVectorIndex = [i for i in range(len(self.dIndexVector))]
    if shuffle:
      random.shuffle(self.randomVectorIndex)
    # self.SpawnRes([], MapEnum.GIFT)
    # self.SpawnRes([], MapEnum.GIFT)
    # self.SpawnRes([], MapEnum.GIFT)

    @self.route('/<path:path>', methods=['POST'])
    def Post(path):
      # print('Post.path=', path)
      if path == 'update':
        return jsonify(
          {'map': self.map, 'last_loss_t': self.last_loss_t, 't': self.t, 'dIndexVector': self.dIndexVector}
        )
      elif path == 'add_ai':
        aiPos = self.GetAiPos()
        if aiPos is None:
          self.map[2][2] = MapEnum.AI
        self.last_loss_t = 0
        self.t = 0
      elif path == 'move':
        # print('type=', type(request.json))
        d = request.json['d']
        v = self.dIndexVector[d]
        self.ai['move'] = [1.0 if i == d else 0.0 for i in range(9)]
        self.AiMove(v)
      elif path == 'see':
        pass
      return jsonify(self.AiSee())

    @self.route('/<path:path>', methods=['Get'])
    def Get(path):
      print('Get.path=', path)
      # print('Get.data=', request.json)
      if path == 'favicon.ico':
        # print('404')
        return 'not found', 404
      return render_template('index.html', map=self.map)

    @self.route('/', methods=['Get', 'POST'])
    def Null():
      return Get('')

  def GetAiPos(self):
    for y in range(len(self.map)):
      for x in range(len(self.map[0])):
        if self.map[y][x] == MapEnum.AI:
          return x, y
    return None

  def AiSee(self):
    x, y = self.GetAiPos()
    see = []
    # print('self.map=', self.map)
    # print('self.GetAiPos=', x, y)
    for vid in self.randomVectorIndex:
      v = self.dIndexVector[vid]
      if v[0] == 0 and v[1] == 0:
        continue
      seePosX = (x + v[0]) % len(self.map[0])
      seePosY = (y + v[1]) % len(self.map)

      # s = [1.0 if i == self.map[seePosY][seePosX] else 0.0 for i in range(3)]
      #
      # see += s
      # print('v, seePosX, seePosY=', v, seePosX, seePosY)
      see.append(self.map[seePosY][seePosX])
    # print('see=', see)
    return {'frame': see, 'y': self.ai['y']}

  def AiMove(self, v):
    self.t += 1
    x, y = self.GetAiPos()
    newX = (x + v[0]) % len(self.map[0])
    newY = (y + v[1]) % len(self.map)
    self.ai['y'] = 0.0
    if v[0] != 0.0 or v[1] != 0.0:
      self.ai['y'] = -0.001
    else:
      self.ai['y'] = -0.002
    gotoMapType = self.map[newY][newX]
    self.map[y][x] = 0
    self.map[newY][newX] = MapEnum.AI
    if gotoMapType == MapEnum.GIFT:
      self.ai['y'] = 1.0
      # newGiftX, newGiftY = self.GetRandNullPos([(newX, newY)])
      # self.map[newGiftY][newGiftX] = MapEnum.GIFT
      self.SpawnRes([(newX, newY)], MapEnum.GIFT)
    elif gotoMapType == MapEnum.DAMAGE:
      self.ai['y'] = -1.0
      self.last_loss_t = self.t
      # newDamageX, newDamageY = self.GetRandNullPos([(newX, newY)])
      # self.map[newDamageY][newDamageX] = MapEnum.DAMAGE
      self.SpawnRes([(newX, newY)], MapEnum.DAMAGE)

  def SpawnRes(self, filterPos, mapType):
    newX, newY = self.GetRandNullPos(filterPos)
    self.map[newY][newX] = mapType

  def GetRandNullPos(self, filterPos):
    posList = []
    for y in range(len(self.map)):
      for x in range(len(self.map[0])):
        if (x, y) in filterPos:
          continue
        if self.map[y][x] != MapEnum.NULL:
          continue
        posList.append((x, y))
    return random.choice(posList)

  def Run(self):
    self.run(port=5011)


if __name__ == '__main__':
  # w = World(12, 12, 5, 30, D_INDEX_VECTOR_16)
  w = World()
  w.Run()
